package com.emotion.recognition.server.mlp;

import com.google.common.base.Objects;
import com.google.common.base.Preconditions;

/**
 * A weighted edge between two perceptrons.
 * 
 * @author Minsang
 */
public final class Edge {

    private final Node source;
    private double weight;
    private final Perceptron destination;
    private double momentum;

    /**
     * Connects two perceptrons, and lets them know about the incoming / outgoing edges.
     */
    public static Edge connect(Node source, Perceptron destination, double weight) {
        return new Edge(source, destination, weight);
    }

    private Edge(Node source, Perceptron destination, double weight) {
        Preconditions.checkNotNull(source);
        Preconditions.checkNotNull(destination);

        this.source = source;
        this.destination = destination;
        setWeight(weight);

        // Let the source perceptron know about this edge
        source.addOutgoingEdge(this);

        // Let the destination perceptron know about this edge
        destination.addIncomingEdge(this);
    }

    /**
     * Provide the input, adjust with the weight, and propagate to the destination perceptron.
     */
    public void propagate(double input) {
        destination.provideInput(this, input * weight);
    }

    //
    // Getters and setters below
    //

    public double getWeight() {
        return weight;
    }

    public void updateWeight(double delta, double momentumRate) {
        delta += momentumRate * momentum;
        this.weight += delta;

        // Update momentum
        momentum = delta;
    }

    public void setWeight(double weight) {
        this.weight = weight;
    }

    public Node getSource() {
        return source;
    }

    public Perceptron getDestination() {
        return destination;
    }

    //
    // hashCode, equals, and toString below
    //

    @Override
    public int hashCode() {
        return Objects.hashCode(source, weight, destination);
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Edge)) {
            return false;
        }
        Edge that = (Edge) obj;
        return Objects.equal(source, that.source) && Objects.equal(weight, that.weight)
                && Objects.equal(destination, that.destination);
    }

    @Override
    public String toString() {
        return String.format("[%s]:[%f]:[%s]", source, weight, destination).toString();
    }
}
